using Godot;
using System;
using CurrentGame.Utils.Interpolation;

namespace CurrentGame.NPC;


public partial class ShadeCharacter : StaticBody2D
{
    [Export]
    public float TeleportScale { get; set; } = 1.5f;

    [ExportGroup("Eye animation")]
    [Export]
    public float EyeTrackFactor { get; set; } = 100.0f;
    [Export]
    public float EyeMoveRange { get; set; } = 5.0f;
    [Export]
    public Vector2I EyeBlinkIntervalRange { get; set; } = Vector2I.Zero;


    public Guid ShadeID;
    

    private Player _player;

    private LerpInterpolation _lerpScale, _lerpEye;
    private CollisionShape2D _headCollision;
    private ShadeParticleEmitter _particleEmitter;
    private Area2D _shadeFootLeft, _shadeFootRight;
    private AnimationPlayer _blinkAnimationPlayer;
    private Timer _blinkTimer;
    private Sprite2D _headSprite, _leftEyeSprite, _rightEyeSprite;

    private bool _playerJumping = false;
    private int _teleporting = 0;
    private float _savedScale = 0.0f;
    private Vector2 _teleportPosition = Vector2.Zero;
    private Vector2 _positionFallback;
    private bool _fallDetection = false;

    // Eye tracking
    private bool _eyeTracking = false;
    private Vector2 _savedLeft, _savedRight;

    public ShadeCharacter()
    {
        ShadeID = Guid.NewGuid();
        _lerpScale = new(0.01f, new Vector3(-1f, 0.001f, -1f))
        {
            Weight = 0.2f
        };
        _lerpEye = new LerpInterpolation(0.0167f, new Vector3(0.01f, -1f, 0.001f))
        {
            Weight = 0.2f
        };
        _lerpScale.ReachStableValue += Signal_InterpolationStable;
    }

    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
	{
        var map = GetNode<NodeReferenceMap>(Constants.NodeRefMapPath);
        _player = map.MainPlayer;

        _headSprite = GetNode<Sprite2D>("Head");
        _blinkTimer = GetNode<Timer>("BlinkTimer");
        _leftEyeSprite = GetNode<Sprite2D>("Head/LeftEye");
        _shadeFootLeft = GetNode<Area2D>("FootLeftDetect");
        _rightEyeSprite = GetNode<Sprite2D>("Head/RightEye");
        _shadeFootRight = GetNode<Area2D>("FootRightDetect");
        _headCollision = GetNode<CollisionShape2D>("CollisionShape2D");
        _blinkAnimationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
        _particleEmitter = GetNode<ShadeParticleEmitter>("ShadeParticleEmitter");

        _lerpScale.Vector2Value = new Vector2(Scale.X, 1.0f);
        _lerpScale.Teleport();
        _savedScale = Scale.X;

        _positionFallback = GlobalPosition;
        _savedLeft = _leftEyeSprite.Position;
        _savedRight = _rightEyeSprite.Position;
        _lerpEye.Value = _savedLeft.X;
        _lerpEye.Vector3Value = new Vector3(_savedLeft.Y, _savedRight.X, _savedRight.Y);
        _lerpEye.Teleport();

        // Start blink Animation
        _blinkTimer.Timeout += Signal_BlinkTimerTimeout;
        var interval = GD.RandRange(EyeBlinkIntervalRange.X, EyeBlinkIntervalRange.Y);
        _blinkTimer.WaitTime = interval;
        _blinkTimer.Start();
    }

    public override void _Process(double delta)
    {
        if (_lerpScale.Process(delta))
        {
            var current = _lerpScale.Vector2Value;
            Scale = new Vector2(current.X, current.X);
            Modulate = new Color(1.0f, 1.0f, 1.0f, current.Y);
        }
        if (_lerpEye.Process(delta))
        {
            var cv = _lerpEye.Value;
            var cv3 = _lerpEye.Vector3Value;
            _leftEyeSprite.Position = new Vector2(cv, cv3.X);
            _rightEyeSprite.Position = new Vector2(cv3.Y, cv3.Z);
        }
    }

    public override void _PhysicsProcess(double delta)
    {
        // Only maintain position while we're not traveling.
        if (_teleporting == 0 && _fallDetection)
        {
            var leftOnGround = _shadeFootLeft.HasOverlappingAreas() || _shadeFootLeft.HasOverlappingBodies();
            var rightOnGround = _shadeFootRight.HasOverlappingAreas() || _shadeFootRight.HasOverlappingBodies();
            // This shade is partially / fully in Air
            if (!(leftOnGround && rightOnGround))
            {
                TeleportTo(_positionFallback);
            }
        }

        // Eye Tracking Animation - no interpolation
        if (_eyeTracking)
        {
            var currentCenter = _headSprite.GlobalPosition;
            var differenceL = _player.GlobalPosition - (currentCenter + _savedLeft);
            var differenceR = _player.GlobalPosition - (currentCenter - _savedRight);
            differenceL *= EyeMoveRange / EyeTrackFactor;
            differenceR *= EyeMoveRange / EyeTrackFactor;

            var leftPos = _savedLeft + differenceL;
            var rightPos = _savedRight + differenceR;

            _lerpEye.Value = leftPos.X;
            _lerpEye.Vector3Value = new Vector3(leftPos.Y, rightPos.X, rightPos.Y);
        }
    }

    public void DisableHeadCollision()
    {
        _headCollision.SetDeferred(CollisionShape2D.PropertyName.Disabled, true);
    }

    public void EnableHeadCollision()
    {
        _headCollision.SetDeferred(CollisionShape2D.PropertyName.Disabled, false);
    }

    public void TeleportTo(Vector2 position)
    {
        _teleportPosition = position;
        _teleporting = 1;
        _savedScale = Scale.X;      // Save current scale
        _lerpScale.Vector2Value = new Vector2(TeleportScale, 0.0f);
        _particleEmitter.CreateTeleportBeginParticle();
    }

    public void ActivateFallDetection()
    {
        _fallDetection = true;
    }

    public void UpdatePositionFallback(Vector2 position)
    {
        _positionFallback = position;
    }

    public void UpdatePositionFallback()
    {
        _positionFallback = GlobalPosition;
    }

    // Private functions
    private void Signal_InterpolationStable(float v, Vector2 v2, Vector3 v3)
    {
        if (_teleporting == 1)
        {
            // Teleport animation finished, perform real teleport.
            GlobalPosition = _teleportPosition;
            ResetPhysicsInterpolation();
            // Ignore next stable (teleport end)
            _teleporting = -1;
            // Start teleport end animation
            _particleEmitter.CreateTeleportEndParticle();
            _lerpScale.Vector2Value = new Vector2(_savedScale, 1.0f);
        }
        else if (_teleporting == -1)
        {
            // Teleport end animation is end, fully end teleport.
            _teleporting = 0;
        }
    }

    private void Signal_BlinkTimerTimeout()
    {
        // Play Animation
        _blinkAnimationPlayer.Play("blink");

        var interval = GD.RandRange(EyeBlinkIntervalRange.X, EyeBlinkIntervalRange.Y);
        _blinkTimer.WaitTime = interval;
        _blinkTimer.Start();
    }

    private void Signal_PlayerEnterTrack(Area2D area)
    {
        _eyeTracking = true;
    }
    
    private void Signal_PlayerLeaveTrack(Area2D area)
    {
        _lerpEye.Value = _savedLeft.X;
        _lerpEye.Vector3Value = new Vector3(_savedLeft.Y, _savedRight.X, _savedRight.Y);
        _eyeTracking = false;
    }
}
